package qc.module.platform.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import qc.common.core.enums.ClientTypeEnum;
import qc.common.core.enums.QCAuthValidateResultEnum;
import qc.common.core.exception.QCPromptException;
import qc.common.core.unify.QCUnifyReturnValue;
import qc.common.core.utils.DateUtil;
import qc.module.platform.dto.log.UserLoginLogDto;
import qc.module.platform.dto.log.UserLoginLogQueryConditionDto;
import qc.module.platform.entity.QcUserloginLogs;
import qc.module.platform.mapper.UserloginLogsMapper;
import qc.module.platform.repository.QcUserloginLogsRepository;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * UserLoginLogService
 *
 * @author QuCheng Tech
 * @create 2023/1/29
 */
@Service
public class UserLoginLogService {
    private QcUserloginLogsRepository repository;

    @Autowired
    public void setQcUserloginLogsRepository(QcUserloginLogsRepository repository) {
        this.repository = repository;
    }

    /**
     * 添加登录日志记录
     *
     * @param log 登录日志
     * @return java.lang.String
     * @author QuCheng Tech
     * @date 2023/1/29
     */
    public String addLog(QcUserloginLogs log) {
        if (repository.insert(log) < 0x1)
            return QCUnifyReturnValue.Warn("保存失败");

        return QCUnifyReturnValue.Success();
    }

    /**
     * 根据token获取用户ID，不管token是否过期，token不存在时返回null
     *
     * @param token 用户登录时得到的token
     * @return java.lang.Integer
     * @author QuCheng Tech
     * @date 2023/1/11
     */
    public Integer getUserIdByToken(String token) {
        return getUserIdByToken(token, false);
    }

    /**
     * 根据token获取用户ID，根据条件判断token是否有效
     *
     * @param token        用户登录时得到的token
     * @param isTokenValid token是否有效判断，如为true进行token有效性判断
     * @return java.lang.Integer
     * @author QuCheng Tech
     * @date 2023/1/11
     */
    public Integer getUserIdByToken(String token, boolean isTokenValid) {
        QcUserloginLogs logs = getUserLoginLogByToken(token);
        if (logs != null) {
            if (isTokenValid) {
                //判断token是否有效
                //先判断登录结果和UID，再判断token是否过期
                if (logs.getStatus() == QCAuthValidateResultEnum.SUCCESS) {
                    if (logs.getUid() != null) {
                        //判断token是否过期
                        if (logs.getExpiredtm() != null) {
                            if (logs.getExpiredtm().after(DateUtil.getNowDate()))
                                return logs.getUid();
                        }
                    }
                }
            } else {
                //不用判断token是否有效，直接返回即可，登录失败时记录中没有UID即为null
                return logs.getUid();
            }
        }

        return null;
    }

    /**
     * 根据token获取用户登录记录信息
     *
     * @param token 用户登录后的token
     * @return qc.module.platform.entity.QcUserloginLogs
     * @author QuCheng Tech
     * @date 2023/1/11
     */
    public QcUserloginLogs getUserLoginLogByToken(String token) {
        //如输入的token为空，返回null
        if (!StringUtils.isBlank(token)) {
            LambdaQueryWrapper<QcUserloginLogs> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(QcUserloginLogs::getToken, token);

            wrapper.orderByDesc(QcUserloginLogs::getTm);

            QcUserloginLogs logs = repository.selectOne(wrapper);
            if (logs != null)
                return logs;
        }

        return null;
    }

    /**
     * 根据token获取用户登录记录信息
     *
     * @param token        用户登录时得到的token
     * @param isTokenValid token是否有效判断，如为true进行token有效性判断
     * @return qc.module.platform.entity.QcUserloginLogs
     * @author QuCheng Tech
     * @date 2023/1/11
     */
    public QcUserloginLogs getUserLoginLogByToken(String token, boolean isTokenValid) {
        //如输入的token为空，返回null
        if (!StringUtils.isBlank(token)) {
            LambdaQueryWrapper<QcUserloginLogs> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(QcUserloginLogs::getToken, token);

            wrapper.orderByDesc(QcUserloginLogs::getTm);

            QcUserloginLogs logs = repository.selectOne(wrapper);
            if (logs != null) {
                if (isTokenValid) {
                    //判断token是否有效
                    //先判断登录结果和UID，再判断token是否过期
                    if (logs.getStatus() == QCAuthValidateResultEnum.SUCCESS) {
                        if (logs.getUid() != null) {
                            //判断token是否过期
                            if (logs.getExpiredtm() != null) {
                                if (logs.getExpiredtm().after(DateUtil.getNowDate()))
                                    return logs;
                            }
                        }
                    }
                } else {
                    //不用判断token是否有效，直接返回即可，登录失败时记录中没有UID即为null
                    return logs;
                }
            }
        }

        return null;
    }

    /**
     * 查询登陆日志
     *
     * @param dto UserLoginLogQueryConditionDto
     * @return 用户登陆日志集合
     * @author QcCheng Tech
     * @since 2023/2/8
     */
    public List<UserLoginLogDto> query(UserLoginLogQueryConditionDto dto) throws Exception {
        if (dto == null)
            throw new QCPromptException("传入对象为空");
        if (StringUtils.isBlank(dto.getBegintm()))
            throw new QCPromptException("开始时间不能为空");
        if (StringUtils.isBlank(dto.getEndtm()))
            throw new QCPromptException("结束时间不能为空");
        if (DateUtil.parseDate(dto.getBegintm()).after(DateUtil.parseDate(dto.getEndtm())))
            throw new QCPromptException("开始时间大于结束时间");

        LambdaQueryWrapper<QcUserloginLogs> wrapper = new LambdaQueryWrapper<>();

        wrapper.between(QcUserloginLogs::getTm, dto.getBegintm(), dto.getEndtm());
        if (!StringUtils.isBlank(dto.getUid()))
            wrapper.eq(QcUserloginLogs::getUid, dto.getUid());
        if (!StringUtils.isBlank(dto.getWords()))
            wrapper.and(k -> k.like(QcUserloginLogs::getUcode, dto.getWords()).or().like(QcUserloginLogs::getClientinfo, dto.getWords()).or().like(QcUserloginLogs::getMsg, dto.getWords()));
        //结果降序排序
        wrapper.orderByDesc(QcUserloginLogs::getTm);

        List<QcUserloginLogs> result = repository.selectList(wrapper);

        if (result.size() > 0x0)
            return UserloginLogsMapper.MAPPER.toDtoList(result);

        return null;
    }

    /**
     * 根据id获取登录日志详情
     *
     * @param id
     * @return 登录日志详情
     * @author QcCheng Tech
     * @since 2023/2/8
     */
    public UserLoginLogDto get(String id) throws Exception {
        if (StringUtils.isBlank(id))
            throw new QCPromptException("日志id不能为空");

        LambdaQueryWrapper<QcUserloginLogs> wrapper = new LambdaQueryWrapper<>();

        wrapper.eq(QcUserloginLogs::getId, id);
        QcUserloginLogs log = repository.selectOne(wrapper);

        if (log == null)
            throw new QCPromptException("日志不存在");

        return UserloginLogsMapper.MAPPER.toDto(log);
    }

    /**
     * 根据用户id以及时间范围查询用户登录过的token
     *
     * @param beginTm 开始时间
     * @param endTm   结束时间
     * @param uid     用户id
     * @return String类型集合
     * @author QcCheng Tech
     * @since 2023/2/8
     */
    public List<String> getTokensByUid(String beginTm, String endTm, String uid) throws Exception {
        if (StringUtils.isBlank(beginTm))
            throw new QCPromptException("开始时间不能为空");
        if (StringUtils.isBlank(endTm))
            throw new QCPromptException("结束时间不能为空");
        if (DateUtil.parseDate(beginTm).after(DateUtil.parseDate(endTm)))
            throw new QCPromptException("开始时间大于结束时间");
        if (StringUtils.isBlank(uid))
            throw new QCPromptException("用户id不能为空");

        LambdaQueryWrapper<QcUserloginLogs> wrapper = new LambdaQueryWrapper<>();

        wrapper.select(QcUserloginLogs::getToken);

        wrapper.between(QcUserloginLogs::getTm, beginTm, endTm).eq(QcUserloginLogs::getUid, uid);

        List<QcUserloginLogs> logList = repository.selectList(wrapper);

        List<String> tokenList = new ArrayList<>();

        if (logList.size() > 0x0) {
            for (int i = 0; i < logList.size(); i++) {
                tokenList.add(logList.get(i).getToken());
            }
            return tokenList;
        }

        return null;
    }

    /**
     * 根据用户id查询用户登录过的token
     *
     * @param uid 用户id
     * @return 用户登录过的token列表
     * @throws QCPromptException
     * @author QuCheng Tech
     * @since 2023/7/7
     */
    public List<String> getTokensByUid(String uid) throws QCPromptException {
        if (StringUtils.isBlank(uid))
            throw new QCPromptException("用户id不能为空");

        LambdaQueryWrapper<QcUserloginLogs> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(QcUserloginLogs::getToken).eq(QcUserloginLogs::getUid, uid);
        List<QcUserloginLogs> logList = repository.selectList(wrapper);

        List<String> tokens = new ArrayList<>();

        if (logList.size() > 0x0) {
            logList.forEach(log -> tokens.add(log.getToken()));
        }
        return tokens;
    }

    /***
     * 设置指定用户指定客户端类型的历史token为过期
     *
     * @param userId 用户ID
     * @param clientType 客户端类型
     * @param beforeTime 小于的时间，在此时间前的登录记录
     * @param excludeToken 需要排除在外的token，可以为空
     * @return void
     * @author QuCheng Tech
     * @since 2023/11/13
     */
    public void setTokenExpired(Integer userId, ClientTypeEnum clientType, Date beforeTime, String excludeToken) {
        if (userId != null) {
            LambdaUpdateWrapper<QcUserloginLogs> wrapper = new LambdaUpdateWrapper<>();
            wrapper.set(QcUserloginLogs::getExpiredtm, beforeTime);

            wrapper.eq(QcUserloginLogs::getUid, userId);
            wrapper.eq(QcUserloginLogs::getClienttype, clientType);
            wrapper.lt(QcUserloginLogs::getTm, beforeTime);
            if (!StringUtils.isBlank(excludeToken))
                wrapper.ne(QcUserloginLogs::getToken, excludeToken);
            //过期时间为空或大于当前时间
            wrapper.and((p) -> {
                p.isNull(QcUserloginLogs::getExpiredtm).or().ge(QcUserloginLogs::getExpiredtm, beforeTime);
            });

            repository.update(null, wrapper);
        }
    }
}

